#-*- coding:utf-8 -*-
'''
Created on 2012-7-11

@author: johnny
'''
import Queue, json, os.path, threading, datetime, time
import listener, vm, hosts
import storage, vmd_port
from db import databases
from utils import zmq_client
from utils import base_utils
from errors import error

STEP_INTERVAL = 5
BASE_MSG = {
            'id': '',
            'action': 0,
            'option_type': 'VmnoclusterchangeTask',
            'param': {}
            }
#default zmq socket port
DEFAULT_MSG_PORT = 9166
#global result queue
LISTENING_QUEUE = Queue.Queue()

class Message():
    def __init__(self):
        self.msg = BASE_MSG
        self.param = {}
    
    def set_param(self, param):
        self.set_value('param', param)
    
    def add_param(self, key, value):
        self.param[key] = value
        self.set_param('param', self.param)
    
    def set_value(self, key, value):
        if key in self.msg.keys():
            self.msg[key] = value
        else:
            print "BuildMsg -- set_value: invalid key"
    
    def get_msg(self):
        return json.dumps(self.msg)
    
class Executer(object):
    def __init__(self):
        #self.result = Queue.Queue()
        self.terminate = threading.Event()
        try:
            self.listener = listener.Listener(LISTENING_QUEUE)
            self.listener.setDaemon(True)
            self.listener.start()
        except Exception:
            pass
    
    def insert_misson(self, host, action, opt_obj_type, opt_type,
                      param, progress=0, task_exe_stat='working',
                      username='admin', display=1):
        query = databases.DBQuery(host)
        #Here may cause bug
        mission_id = -1
        if query.get_mission_id_min() != None:
            if query.get_mission_id_min() <= 0:
                mission_id = query.get_mission_id_min() - 1
            #mission_id = query.get_mission_id_max() + 1
        #avoid conflict with web counter
                    
        keys = """(
                   id,
                   action,
                   optobjtype,
                   progress, 
                   task_execute_stat,
                   username,
                   display,
                   option_type,
                   created_at,
                   param,
                   user_ip
                  )"""
        #Wrong method, it's OK, all fake.
        create_time = datetime.datetime.now().strftime("%Y%m%d %H:%M:%S.%f")
        user_ip = base_utils.get_local_ip()
        values = (
                  mission_id,
                  action,
                  opt_obj_type,
                  progress,
                  task_exe_stat,
                  username,
                  display,
                  opt_type,
                  create_time,
                  param,
                  user_ip
                  )
        
        query.insert('missions', keys, values)
        return mission_id
            
    def run(self, action, params, host, obj_type,
            opt_type, port=DEFAULT_MSG_PORT):
        message = Message()
        message.set_param(params)
        message.set_value('action', action)
        message.set_value('option_type', opt_type)
        
        mission_id = self.insert_misson(host, action, obj_type,
                                        opt_type, str(params))
        message.set_value('id', mission_id)
        print message.get_msg()
        
        time.sleep(STEP_INTERVAL)
        self.send(message.get_msg(), host, port)
        return mission_id
        
    def send(self, message, host, port):
        #sending
        self.recv_msg = zmq_client.zmq_sender(message, host, port)
        return self.recv_msg
    
    def wait_for_done(self, mission_id, host, timeout=60):
        """
        wait action finished,
        mission_id: the mission id for action
        host: which host the task is running
        timeout: wait timeout
        """
        query = databases.DBQuery(host)
        while True:
            try:
                msg = LISTENING_QUEUE.get(True, timeout)
            except Queue.Empty:
                raise error.OperationTimeoutException(timeout)
            if ((msg.has_key('action') or msg.has_key('option_result')) 
                 and msg.has_key('id')):
                if (mission_id == msg['id'] and 
                    'actionResult' == msg['message']):
                    #What if action failed? 
                    if 'failed' == query.get_mission_stat(mission_id):
                        err_msg = query.get_mission_message(mission_id)
                        raise error.TestError("Action Failed: %s" % err_msg)
                    
                    if msg.has_key('option_result'):
                        return msg['option_result']
                    
                    return True
                #Just put it back if mission id is not match, someone must need it
                else:
                    #All missions start from code have a negative id
                    if msg['id'] < 0:
                        #print "putting msg ", msg
                        LISTENING_QUEUE.put(msg, timeout=timeout)
                
    def do(self):
        #wrapper for run and wait_for_done
        pass
            
 
class Manager(object):
    def __init__(self):
        self.executor = Executer()
            
    def create(self, name, host, storage_name='defaultlocal',
               cfg=None, img=None):
        #avoid duplicated name
        if self.is_name_dupliacted(name, host):
            raise error.CommonError("Create vm: name duplicated!")
        
        m_vm = vm.VMS(name)
        
        #Set vm config info
        if cfg:
            m_vm.update_cfg(cfg)
        
        m_vm.cfg['storage_path'] = self.get_storage_path(host, storage_name)
        m_vm.cfg['storage_uuid'] = self.get_storage_uuid(host, storage_name)
        m_vm.cfg['hostUuid'] = self.get_host_uuid(host)
        
        if not img:
            m_vm.add_hd()
        else:
            vhd = {
                   'baseimg': img,
                   'createvhd': 'no'
                   }
            m_vm.add_hd(vhd)
                   
        m_vm.add_netcard()
        action_id = vmd_port.action_code['create_vms']      
        m_id = self.executor.run(action_id, m_vm.cfg, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host, 120)
        m_vm.initialize(host, self.executor)
        return m_vm
    
    def clone_vm(self, source, dest_name, dest_st, level='high', cfg=None):
        params = {}
        #unnecessary
        params.update(vmd_port.CLONE_VMS)
        host_name = source.get_hostname()
        
        m_vm = vm.VMS(dest_name)
        
        m_vm.update_cfg(source.cfg)
        m_vm.cfg['storage_path'] = self.get_storage_path(host_name, dest_st)
        m_vm.cfg['storage_uuid'] = self.get_storage_uuid(host_name, dest_st)
        m_vm.cfg['description'] = dest_name
        #is that necessary?
        m_vm.update_vhd({'baseimg': ''})
        
        if cfg:
            params.update(cfg)
        else:
            dest_path = storage.get_storage_path(host_name, dest_st)
            dest_uuid = storage.get_storage_uuid(host_name, dest_st)
            params.update({
                       'parentUuid': source.cfg['hostUuid'],
                       'targetVmdesc': dest_name,
                       'targetVmuuid': dest_name,
                       'bsstorage_uuid': source.cfg['storage_uuid'],
                       'bsstorage_path': source.cfg['storage_path'],
                       'vmUuid': source.get_vmUuid(),
                       'storage_path': dest_path,
                       'storageUuid': dest_uuid,
                       'copy_level': level
                       })
        
        action_id = vmd_port.action_code['clone_vms']    
        m_id = self.executor.run(action_id, params, host_name,
                                 'vm', 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host_name, 120)
        m_vm.initialize(host_name, self.executor)
        baseimg = m_vm.get_baseimg()
        m_vm.update_vhd({'baseimg': baseimg})
        return m_vm
    
    def snapshot(self, vm, sname, desc=""):
        param = {}
        param.update(vmd_port.TAKE_SNAPSHOT)
        param['parentUuid'] = vm.get_parentUuid()
        param['storage_uuid'] = vm.get_storage_uuid()
        param['storage_path'] = vm.get_storage_path()
        param['vmUuid'] = vm.get_vmUuid()
        param['snname'] = sname
        param['sndesc'] = desc
        host = vm.get_hostname()
        
        action_id = vmd_port.action_code['snapshot']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)
    
    def recover_snapshot(self, vm, sn):
        param = {}
        param.update(vmd_port.RECOVER_SNAPSHOT)
        
        param['parentUuid'] = vm.get_parentUuid()
        param['storage_uuid'] = vm.get_storage_uuid()
        param['storage_path'] = vm.get_storage_path()
        param['vmUuid'] = vm.get_vmUuid()
        param['snuuid'] = sn
        host = vm.get_hostname()
        
        action_id = vmd_port.action_code['re_snapshot']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)
    
    def conv_to_template(self, vm):
        param = {}
        
        param['vmUuid'] = vm.get_vmUuid()
        param['parentUuid'] = vm.get_parentUuid()
        param['storage_path'] = vm.get_storage_path()
        host = vm.get_hostname()
        
        action_id = vmd_port.action_code['vm2temp']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)
        
    def dele_template(self, tp):
        param = {}
        
        param['vmUuid'] = tp.get_vmUuid()
        param['parentUuid'] = tp.get_parentUuid()
        param['storage_path'] = tp.get_storage_path()
        param['copy_level'] = ""
        host = tp.get_hostname()
        
        action_id = vmd_port.action_code['delete_temp']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)
    
    def puase_vm(self, vm):
        param = {}
        
        param['vmUuid'] = vm.get_vmUuid()
        param['parentUuid'] = vm.get_parentUuid()
        param['storage_path'] = vm.get_storage_path()
        host = vm.get_hostname()
        
        action_id = vmd_port.action_code['vm_pause']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)
    
    def trans_vm(self, vm, storage, level="low", target=None):
        param = {}
        
        param['vmUuid'] = vm.get_vmUuid()
        param['storage_path'] = vm.get_storage_path()
        param['target_storage_path'] = vm.host.get_storage_path(storage)
        param['copy_level'] = level
        if target:
            param['target'] = target
        host = vm.get_hostname()
        
        action_id = vmd_port.action_code['vm_trans']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host)

    def diff_clone(self, tp, basename, typ=0, number=1, inherit_net='yes'):
        '''
        tp: the name of template
        type: diff clone type, 0 for readonly, 1 for read/write 
        '''
        param = {}
        
        param['vmUuid'] = tp.get_vmUuid()
        param['parentUuid'] = tp.get_parentUuid()
        param['storage_path'] = tp.get_storage_path()
        if typ == 0:
            param['diff_type'] = 'ro_diff'
        elif typ == 1:
            param['diff_type'] = 'rw_diff'
        
        param['base_name'] = basename
        param['totalnum'] = number
        param['inherit_net'] = inherit_net
        host = tp.get_hostname()
        
        action_id = vmd_port.action_code['diff_clone']
        m_id = self.executor.run(action_id, param, host, 'vm',
                                 'VmnoclusterchangeTask')
        
        #return names of cloned vm
        return self.executor.wait_for_done(m_id, host)
    
    def backup_vm(self, vm, dest, tp=0, level="low", 
                  descr="", baktype="manul"):
        params = {}
        
        params['vmUuid'] = vm.get_vmUuid()
        params['uuid'] = vm.get_storage_uuid()
        params['parentUuid'] = vm.get_parentUuid()
        params['storage_path'] = vm.get_storage_path()
        params['copy_Level'] = level
        params['bakdsc'] = descr
        params['baktype'] = baktype
        
        if vm.host.get_storage_path(dest):
            params['targetstorage_path'] = vm.host.get_storage_path(dest)
        else:
            raise error.CommonError("Target storage does not exist.")
        if not tp:
            params['backup_type'] = "diff_backup"
        elif tp == 1:
            params['backup_type'] = "complete_backup"
        
        action_id = vmd_port.action_code['backup_vm']
        host = vm.get_hostname()
        m_id = self.executor.run(action_id, params, host, 'vm',
                                 'VmnoclusterchangeTask')
        
        self.executor.wait_for_done(m_id, host, timeout=1800)
    
    def delete_vm(self, host, vm, del_vhd=False):
        params = {}
        self.vm = vm
        params['vmUuid'] = self.vm.get_vmUuid()
        params['storage_path'] = self.vm.get_parentUuid()
        params['parentUuid'] = self.vm.cfg['hostUuid']
        if del_vhd:
            params['delvhd'] = 'delvhd'
        else:
            params['delvhd'] = 'savevhd'
            
        action_id = vmd_port.action_code['delete_vms']
        m_id = self.executor.run(action_id, params, host,
                                 'vm', 'VmnoclusterchangeTask')
        self.executor.wait_for_done(m_id, host, 120)
    
    def is_name_dupliacted(self, name, host):
        query = databases.DBQuery(host)
        if name in query.query('vms', column='description'):
            return True
        else:
            return False
    
    def check_img(self, vm):
        img = os.path.basename(vm.get_baseimg())
        storage_path = vm.get_storage_path()
        result = vm.host.run("find %s -name %s" % 
                             (storage_path, img)).stdout.strip()
        
        if vm.get_baseimg() not in result:
            raise error.TestError("Check create: Cannot find image file")
    
    def check_clone(self, vm):
        img = os.path.basename(vm.get_baseimg())
        storage_path = vm.get_storage_path()
        result = vm.host.run("find %s -name %s" % 
                             (storage_path, img)).stdout.strip()
        
        if vm.get_baseimg() not in result:
            raise error.TestError("VERIFY clone: Cannot find image file")
     
    def get_img_path(self, host, storage, name):
        query = databases.DBQuery(host, user="postgres")
        keyword = "description=\'" + storage + "\'"
        path = os.path.join(query.get_storagepath(keyword), name)
        path = path.replace('\\', '/')
        return path   
        
    def get_storage_path(self, host, storage_name):
        return storage.get_storage_path(host, storage_name)
        
    def get_storage_uuid(self, host, storage_name):
        return storage.get_storage_uuid(host, storage_name)
        
    def get_host_uuid(self, host):
        qurey = databases.DBQuery(host)
        return qurey.get_hostUuid()
        
    def wait_parallel(self, mission_ids, host):
        for i in mission_ids:
            self.executor.wait_for_done(i, host)
        
    